/*
 * manejoFAT.c
 *
 *  Created on: 06/11/2011
 *      Author: utn_so
 */


#include "manejoFAT.h"

/*	variable global, Sectores Reservados, Fat Compies	*/
unsigned char* bootAndFats;
extern pthread_mutex_t mutex_fat;
extern pthread_mutex_t mutex_oper_fat;
extern pthread_mutex_t mutex_asin_arch_disco;
extern FILE* archivoDisco;
extern uint32_t cantidadCLUSTERDATA;


/*	32 sectores reservados + sectores fat size * copies	*/
int32_t bufferPrincipalBootFats(struct bootSector* bootSector, unsigned char* fatVolumeRegion)
{
	/*	FAT32	*/
	/*	FAT32	*/
	/*	FAT32	*/

	uint32_t sectoresPrincipales = firstDataSector(bootSector);
	//	le saco una fat
	sectoresPrincipales -= bootSector ->BPB_FATSz32;

	uint32_t i;
	uint32_t tope = sectoresPrincipales * bootSector -> BPB_BytsPerSec;

	pthread_mutex_lock(&mutex_asin_arch_disco);
	fseek(archivoDisco, 0, SEEK_SET);

	for(i=0; i< tope; i++ )
		fatVolumeRegion[i] = fgetc(archivoDisco);

	pthread_mutex_unlock(&mutex_asin_arch_disco);

	return 0;
}

uint32_t thisFATSecNum(struct bootSector* bootSector, uint32_t cluster)
{
	/*	FAT32	*/
	/*	FAT32	*/
	/*	FAT32	*/

	/*If(FATType == FAT16)
	    FATOffset = N * 2;
	Else if (FATType == FAT32)
	    FATOffset = N * 4;*/

	uint32_t FATOffset = cluster * 4;
	uint32_t ThisFATSecNum = bootSector -> BPB_RsvdSecCnt + (FATOffset / bootSector -> BPB_BytsPerSec);

	return ThisFATSecNum;
}

uint32_t thisFATSecNumOffset(struct bootSector* bootSector, uint32_t cluster)
{
	/*	FAT32	*/
	/*	FAT32	*/
	/*	FAT32	*/

	/*If(FATType == FAT16)
	    FATOffset = N * 2;
	Else if (FATType == FAT32)
	    FATOffset = N * 4;*/

	uint32_t FATOffset = cluster * 4;
	uint32_t ThisFATEntOffset = FATOffset % bootSector -> BPB_BytsPerSec;

	return ThisFATEntOffset;

}

int32_t leerEntradaFAT(struct bootSector* boot, uint32_t cluster)
{
	if(cluster<2)
		return -1;

	int ent = thisFATSecNum(boot, cluster);
	int off = thisFATSecNumOffset(boot, cluster);
	/*	sector + el desplazamiento	*/
	int auxInt = (ent * boot->BPB_BytsPerSec) + off;

	char* auxChar = malloc (sizeof(char) * 20);
	//strcpy(auxChar, "0x");
	auxChar[0]= '0';
	auxChar[1]= 'x';
	auxChar[2]= '\0';

	pthread_mutex_lock(&mutex_oper_fat);

	auxChar[2] = hexToChar2(valorHighChar(bootAndFats[auxInt]));
	auxChar[3] = hexToChar2(0x0F & bootAndFats[auxInt]);
	auxChar[4] = hexToChar2(valorHighChar(bootAndFats[auxInt +1]));
	auxChar[5] = hexToChar2(0x0F & bootAndFats[auxInt +1]);
	auxChar[6] = hexToChar2(valorHighChar(bootAndFats[auxInt +2]));
	auxChar[7] = hexToChar2(0x0F & bootAndFats[auxInt +2]);
	auxChar[8] = hexToChar2(valorHighChar(bootAndFats[auxInt +3]));
	auxChar[9] = hexToChar2(0x0F & bootAndFats[auxInt +3]);
	auxChar[10] = '\0';

	pthread_mutex_unlock(&mutex_oper_fat);

	/*printf("%s\n", auxChar);*/
	uint32_t entradaFAT = swap_uint32(htoi(auxChar));

	/*	they are only 28-bit values	*/
	entradaFAT &= 0x0FFFFFFF;
	/*printf("%02x \n", entradaFAT);*/

	free(auxChar);
	return entradaFAT;

}

int32_t cambiarEntradaFAT(struct bootSector* boot, uint32_t cluster, uint32_t valor)
{

	if(cluster<2)
		return -1;

	/*printf("Valor dado vuelta: %02x\n", cambio);*/

	unsigned char* bufferAux = malloc(sizeof(char)*4);
	ponerHexaEnBuffer(swap_uint32(valor), bufferAux);

	/*int i;
	for(i=0; i<4; i++)
		printf("%02x ",bufferAux[i]);
	printf("\n");*/

	int32_t ent = thisFATSecNum(boot, cluster);
	int32_t off = thisFATSecNumOffset(boot, cluster);
	/*	sector + el desplazamiento	*/
	int auxInt = (ent * boot -> BPB_BytsPerSec) + off;

	pthread_mutex_lock(&mutex_oper_fat);
	bootAndFats[auxInt] = bufferAux[0];
	bootAndFats[auxInt+1] = bufferAux[1];
	bootAndFats[auxInt+2] = bufferAux[2];
	bootAndFats[auxInt+3] = bufferAux[3];
	pthread_mutex_unlock(&mutex_oper_fat);

	free(bufferAux);
	/*	se cambio la fat, hay que persistirla	*/

	return 0;
}

int32_t ponerHexaEnBuffer(uint32_t valor, unsigned char* buffer)
{
	/*	32 bits	*/

	buffer[0] = valor / 0x1000000;
	valor &= 0x00FFFFFF;
	buffer[1] = valor / 0x10000;
	valor &= 0x0000FFFF;
	buffer[2] = valor / 0x100;
	valor &= 0x000000FF;
	buffer[3] = valor;

	return 0;
}

int32_t esClusterLibre(struct bootSector* boot, uint32_t cluster)
{

	uint32_t entradaFAT = leerEntradaFAT(boot, cluster);

	if(entradaFAT == 0x00)
		return 0;

	return -1;
}

int32_t dameClusterLibre(struct bootSector* boot)
{
	/*	Si la funcion que invoca esta funcion pincha,
	 * 	se debe modificar la tabla fat inmediatamente
	 * 	liberando el cluster ya tomado	*/

	pthread_mutex_lock(&mutex_fat);
	uint32_t i;
	//uint32_t auxInt = cantidadClustersData(boot);
	uint32_t auxInt = cantidadCLUSTERDATA;
	auxInt +=2;

	for(i=2; i<auxInt; i++)
		{
		if (esClusterLibre(boot, i) == 0)
				{
				//	No hace falta hacer un lock
				//	se hace cuando se llama a la funcion
				cambiarEntradaFAT(boot, i, 0xFFFFFFFF);
				pthread_mutex_unlock(&mutex_fat);
				return i;
				}
		}

	pthread_mutex_unlock(&mutex_fat);
	/* no hay clusters libres	*/
	return -1;
}

uint32_t mostrarEntradasFat(struct bootSector* boot)
{

	uint32_t i;
	uint32_t auxInt = cantidadClustersData(boot);
	auxInt +=2;

	for(i=2; i<=auxInt; i++)
		printf("Entrada cluster %i: %02x\n", i, leerEntradaFAT(boot, i));

	return 0;
}

uint32_t cantidadClustersLibres(struct bootSector* boot)
{
	int i;
	uint32_t clustersLibres = 0;
	uint32_t totalClusterData = cantidadClustersData(boot);

	/*	se arranca del cluster 2	*/
	totalClusterData+=2;

	for(i=2; i<= totalClusterData; i++ )
		{
		if(!esClusterLibre(boot, i))
		clustersLibres ++;
		}

	return clustersLibres;
}


uint32_t traerElEOC(struct bootSector* boot, unsigned char* fatVolumePricipal)
{
	uint32_t valor;

	uint32_t auxInt = boot -> BPB_RsvdSecCnt * boot -> BPB_BytsPerSec;

	/*	FAT[1] -> FORMAT to the EOC	*/
	auxInt += 4;

	/*printf("%02x ",bootAndFats[16391]);
	printf("%02x ",bootAndFats[16390]);
	printf("%02x ",bootAndFats[16389]);
	printf("%02x \n",bootAndFats[16388]);*/

	char* auxChar = malloc (sizeof(char) * 20);
	strcpy(auxChar, "0x");

	/*	guarda, esta al revez, Little Endian	*/
	strcat(auxChar, hexToChar(valorHighChar(fatVolumePricipal[auxInt])));
	strcat(auxChar, hexToChar(valorLowChar(fatVolumePricipal[auxInt])));
	strcat(auxChar, hexToChar(valorHighChar(fatVolumePricipal[auxInt +1])));
	strcat(auxChar, hexToChar(valorLowChar(fatVolumePricipal[auxInt +1])));
	strcat(auxChar, hexToChar(valorHighChar(fatVolumePricipal[auxInt +2])));
	strcat(auxChar, hexToChar(valorLowChar(fatVolumePricipal[auxInt +2])));
	strcat(auxChar, hexToChar(valorHighChar(fatVolumePricipal[auxInt +3])));
	strcat(auxChar, hexToChar(valorLowChar(fatVolumePricipal[auxInt +3])));

	/*printf("%s \n", auxChar);*/
	valor = swap_uint32(htoi(auxChar));

	free(auxChar);
	return valor;
}

int32_t verificarUltimoClusterCadena(struct bootSector* boot, uint32_t cluster)
{
	/*uint32_t EOC = traerElEOC(boot, fatVolumePricipal);*/

	uint32_t EOCaux = leerEntradaFAT(boot, cluster);

	if((EOCaux >= 0x0FFFFFF8) && (EOCaux <= 0x0FFFFFFF))
		return 0;

/*	else if(EOCaux == 0)
		return 0;*/

	return -1;
}
